package edu.mtu.citizenscience.cs.sdk.uploader.upload.parts;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;

import edu.mtu.citizenscience.cs.sdk.json.Request;
import edu.mtu.citizenscience.cs.sdk.json.Response;
import edu.mtu.citizenscience.cs.sdk.uploader.CitsciUploaderError;
import edu.mtu.citizenscience.cs.sdk.uploader.CitsciUploaderProgress;
import edu.mtu.citizenscience.cs.sdk.uploader.CitsciUploaderService;
import edu.mtu.citizenscience.cs.sdk.uploader.model.Row;
import edu.mtu.citizenscience.cs.sdk.uploader.model.entry.FileEntry;
import edu.mtu.citizenscience.cs.sdk.uploader.upload.UploadPart;
/**
 * @author Corbin Uselton <corbinu@cwuselto@mtu.edu>
 * @copyright (c) 2012 MichiganTech <http://www.mtu.edu>
 * @since 1.0
 * 
 * Uploads a Report to the server
 */
public class Report extends UploadPart {
	// parent reports upload part
	private Reports reports;
	// report row
	private Row row;
	// report num
	private int num;
	// the currently uploading file num
	private int fileNum;
	// the upload id to attach the report to
	private int uploadId;
	// files attached to the report
	private List<FileEntry> files = new ArrayList<FileEntry>();
	
	/**
	 * @since 1.0
	 * 
	 * Report upload response 
	 */
	public static class ReportResponse {
		// report id
		private int id;

		/**
		 * Get report id
		 * 
		 * @param   void
		 * @return	int - report id
		 */
		public int getId() 
		{
			return id;
		}

		/**
		 * Set report id
		 * 
		 * @param   id - int - report id
		 * @return	void
		 */
		public void setId(int id) 
		{
			this.id = id;
		}
	}
	
	/**
	 * @since 1.0
	 * 
	 * Handles report upload response
	 */
	private class ReportResponseHandler extends UploadResponseHandler<ReportResponse> {

		/**
		 * Create Report Response Handler
		 * 
		 * @param   void
		 * @return	void
		 */
		public ReportResponseHandler() 
		{
			super(ReportResponse.class);
		}

		/**
		 * Called on response from the server
		 * 
		 * @param   response - Response<ReportResponse> - the response from the server
		 * @return	void
		 */
		@Override
		public void response(Response<ReportResponse> response) 
		{
			try 
			{
				// make sure server returned a report id
				response.getPayload().getId();
				
				// update the uploader progress with the report num
				uploader.sendProgressUpdate(num);
				
				// upload the next report
				reports.nextReport();
			}
			catch (JsonParseException e) 
			{
				e.printStackTrace();
				// pass the error parsing the response to the uploader
				uploader.sendError(CitsciUploaderError.INVALID_RESPONSE);
			}
			catch (JsonMappingException e) 
			{
				e.printStackTrace();
				// pass the error parsing the response to the uploader
				uploader.sendError(CitsciUploaderError.INVALID_RESPONSE);
			}
		}
		
	}
	
	/**
	 * Create a new Report Upload
	 * 
	 * @param   uploader - CitsciUploaderService - the uploader service uploading the report
	 * @param   reports - Reports - the reports upload part
	 * @param   uploadId - int - the upload id
	 * @param   row - Row - the report row
	 * @return	void
	 */
	public Report(CitsciUploaderService uploader, Reports reports, int uploadId, Row row)
	{
		super(uploader);
		
		this.reports = reports;
		this.uploadId = uploadId;
		this.row = row;
	}
	
	/**
	 * Start the report upload
	 * 
	 * @param   num - int - the report num
	 * @return	void
	 */
	public void startUpload(int num)
	{
		this.num = num;
		// send the progress as uploading report and the number
		this.uploader.sendProgress(CitsciUploaderProgress.REPORT, this.num, -1);
		
		// get the rows child rows
		List<Row> children = this.row.getChildren();
		
		// loop while there are children in the children list
		while (children.size() > 0)
		{
			// create a new row list for newChildren
			List<Row> newChildren = new ArrayList<Row>();
			
			// loop over the child rows
			for (Row row : children)
			{
				// check if the row has an children
				if (row.haveChildren())
				{
					// add the rows children to the new child list
					newChildren.addAll(row.getChildren());
				}
				
				// check if the row has a file entry
				if (row.haveFileEntry())
				{
					// add the file to the report file list
					this.files.add(row.getFileEntry());
				}
			}
			// set the child list to the new set of children to crawl down the child tree
			children = newChildren;
		}
		// set the current file num to 0
		this.fileNum = 0;
		
		// upload the first file
		this.nextFile();
	}
	
	/**
	 * Upload the next report file
	 * 
	 * @param   void
	 * @return	void
	 */
	public void nextFile()
	{
		// check if the current file num is less then the file list size
		if (this.fileNum < this.files.size())
		{
			// create new report file upload part
			new ReportFile(this.uploader, this.uploadId, this, this.files.get(this.fileNum)).startUpload(this.num, this.fileNum + 1);
			// Increment the file num
			this.fileNum++;
		}
		else
		{
			// upload the report
			this.uploadReport();
		}
	}
	
	/**
	 * Upload the report
	 * 
	 * @param   void
	 * @return	void
	 */
	public void uploadReport()
	{
		// set the uploader progress to upload the report data with the report num
		this.uploader.sendProgress(CitsciUploaderProgress.REPORT_DATA, this.num, -1);
		
		try 
		{
			// get the geolocations of the row and its children
			this.row.geoLocateLocations(this.uploader.getAq().getContext());
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
		
		// create a request for the upload_report action with the upload id and the row map
		Request<Map<String, Object>> request = new Request<Map<String, Object>>("upload_report", this.uploadId, this.row.getAsMap());
			
		// send the request to the server passing the report response handler to handle the response
		this.uploader.doRequest(request, new ReportResponseHandler());
	}
}
